//! Parse query str
//!
//! use regex to do it

use crate::{query::Value, Error};
use regex::{Captures, Regex};
use std::convert::TryFrom;

pub type ParseResult = Result<Parser, Error>;

impl From<regex::Error> for Error {
    fn from(e: regex::Error) -> Self {
        Self::Regex(e)
    }
}

#[doc(hidden)]
macro_rules! magic {

    (@take $name:expr, $id:expr) => {{
        std::mem::replace(
            $name.get_mut($id).ok_or(
                magic!(@err "{}[{}] is found to be a none type", stringify!($name), $id)
            )?, Err(
                magic!(@err "{}[{}] is found to be a error type", stringify!($name), $id)
            ))?.into()
    }};

    (@err $($arg:tt)*) => {
        Error::Parse(format!($($arg)*))
    };

    (@regex $op:expr => $capture:expr) => {
        Element::try_from(regex::Regex::new(&format!("\\[(.+)?{}(.+)\\]", $op.print()))?.captures($capture))
    };



}

/// k-v ops
pub enum Op {
    /// op\[=\]
    Eql,
    /// op\[>=\]
    Ege,
    /// op\[<=\]
    Ele,
    /// op\[>\]
    Gre,
    /// op\[<\]
    Les,
    /// op\[=.\]
    Con,
    /// op\[!\]
    Dif,
    /// op\[!.\]
    Nco,
}

macro_rules! op_match_str {
    ($s:expr => $start:expr , $($op:expr$(,)?)* => $end:expr) => {{
        if $s.contains($start.print()) {
            $start
        }
        $( else if $s.contains($op.print()) {$op} )*
        else {
            $end
        }
    }};
}

impl From<&str> for Op {
    fn from(s: &str) -> Self {
        return op_match_str!(
            s =>
            Self::Ege,
            Self::Ele,
            Self::Nco,
            Self::Dif,
            Self::Gre,
            Self::Les,
            Self::Con
            => Self::Eql
        );
    }
}

impl Op {
    pub fn print(&self) -> &str {
        match self {
            Op::Ege => ">=",
            Op::Ele => "<=",
            Op::Nco => "!.",
            Op::Con => "=.",
            Op::Eql => "=",
            Op::Gre => ">",
            Op::Les => "<",
            Op::Dif => "!",
        }
    }
}

impl std::fmt::Debug for Op {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        write!(f, "{}", self.print())
    }
}

pub struct Element(pub(crate) String, pub(crate) Op, pub(crate) Value);

impl std::fmt::Debug for Element {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        write!(f, "{:?} {:?} {:?}", self.0, self.1, self.2)
    }
}

impl TryFrom<Captures<'_>> for Element {
    type Error = Error;

    fn try_from(c: Captures<'_>) -> Result<Self, Self::Error> {
        if c.get(0).is_none() || c.get(1).is_none() || c.get(2).is_none() {
            return Err(magic!(@err "the length of captures is invalid"));
        }

        Ok(Self(c[1].to_string(), Op::from(&c[0]), Value::from(&c[2])))
    }
}

impl TryFrom<Option<Captures<'_>>> for Element {
    type Error = Error;

    fn try_from(c: Option<Captures<'_>>) -> Result<Self, Self::Error> {
        match c {
            Some(c) => Self::try_from(c),
            None => Err(magic!(@err "the capture is none")),
        }
    }
}

/// join the elements
pub enum Junction {
    /// Junc\[+\]
    Or,
    /// Junc\[*\]
    And,
    /// Junc\[-\]
    Diff,
    /// Any unknown Junction
    Null,
}

impl std::fmt::Debug for Junction {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        match self {
            Self::Or => write!(f, "+"),
            Self::And => write!(f, "*"),
            Self::Diff => write!(f, "-"),
            Self::Null => write!(f, "?"),
        }
    }
}

impl From<&str> for Junction {
    fn from(s: &str) -> Self {
        match s {
            "*" | "]*[" => Self::And,
            "-" | "]-[" => Self::Diff,
            "+" | "]+[" | "." | "].[" => Self::Or,
            _ => Self::Null,
        }
    }
}

impl From<Captures<'_>> for Junction {
    fn from(c: Captures) -> Self {
        Self::from(c.get(0).unwrap().as_str())
    }
}

#[derive(Debug)]
#[allow(dead_code)]
/// parser single node
pub enum Node {
    Element(Element),
    Junction(Junction),
}

impl From<Element> for Node {
    fn from(e: Element) -> Self {
        Self::Element(e)
    }
}

impl From<Junction> for Node {
    fn from(e: Junction) -> Self {
        Self::Junction(e)
    }
}

#[derive(Debug)]
/// the parser of query
///
/// using regex to transform str into parser::Node
pub struct Parser {
    pub(crate) nodes: Vec<Node>,
}

impl AsRef<Parser> for Parser {
    fn as_ref(&self) -> &Parser {
        self
    }
}

impl std::ops::Deref for Parser {
    type Target = Vec<Node>;

    fn deref(&self) -> &Self::Target {
        &self.nodes
    }
}

impl From<Vec<Node>> for Parser {
    fn from(nodes: Vec<Node>) -> Self {
        Self { nodes }
    }
}

impl TryFrom<&str> for Parser {
    type Error = Error;

    fn try_from(value: &str) -> Result<Self, Self::Error> {
        Self::try_from(value.to_string())
    }
}

impl TryFrom<String> for Parser {
    type Error = Error;

    fn try_from(q: String) -> Result<Self, Self::Error> {
        // remove all whitespaces
        let mut q = q;
        q.retain(|c| !c.is_ascii_whitespace());

        let mut operator: Vec<Option<Junction>> = Regex::new(r"(\].\[)")?
            .captures_iter(&q)
            .into_iter()
            .map(|c| Some(Junction::from(c)))
            .collect();

        let mut element: Vec<Result<Element, Error>> = Regex::new(r"(\[.+?\])")?
            .captures_iter(&q)
            .into_iter()
            .map(|c| match Op::from(&c[0]) {
                Op::Con => magic!(@regex Op::Con => &c[0]),
                Op::Nco => magic!(@regex Op::Nco => &c[0]),
                Op::Ege => magic!(@regex Op::Ege => &c[0]),
                Op::Ele => magic!(@regex Op::Ele => &c[0]),
                Op::Eql => magic!(@regex Op::Eql => &c[0]),
                Op::Les => magic!(@regex Op::Les => &c[0]),
                Op::Gre => magic!(@regex Op::Gre => &c[0]),
                Op::Dif => magic!(@regex Op::Dif => &c[0]),
            })
            .collect();

        let mut nodes = vec![magic!(@take element, 0)];

        for i in 0..operator.len() {
            nodes.push(
                operator[i]
                    .take()
                    .ok_or(magic!(@err "operator[{}] is found to be a none type", i))?
                    .into(),
            );

            nodes.push(magic!(@take element, i+1));
        }

        Ok(Self::from(nodes))
    }
}
